import Decimal from 'decimal.js';
import Common from './Common.js';
import OkxRestService from './OkxRestService.js';
export default class RoundTripTradeBuyerDefault {
    /**
     * @constructor
     * @param {*} trade
     */
    constructor( trade ) {
        this.trade = trade;
        this.options = trade.options.buy;
        // 当前单价
        this.priceCurrent = null;
        // 基础单价
        this.priceBase = null;
        // 单价降价百分比
        this.priceReductionPercent = new Decimal(0.005); // 市价
        // 目标单价
        this.priceTarget = null;
        // 交易价格
        this.tradeTotalPrice = new Decimal(10); // 10 USDT
        // 买入价格
        this.priceBought = null;
        // 交易单ID
        this.orderId = null;
        // 交易开始时间
        this.orderStartedAt = null;
        // 交易完成时间
        this.orderFilledAt = null;
        // 交易完成数量
        this.orderFilledSize = null;
        // 交易时间限制
        this.orderDurationLimit = 30 * 60 * 1000; // 10分钟
        // 交易价格差限制
        this.orderCancelPriceDiff = 2; // 2倍
        // 订单信息
        this.orderInfo = null;
        // 交易是否取消
        this.isCanceled = false;
        // 取消原因
        this.cancelReason = null;
    }

    /**
     * 生成策略标题
     * @returns {string}
     */
    generateTitle() {
        if ( this.priceReductionPercent.equals(0) ) {
            return '市价';
        }
        let reductionPercent = this.priceReductionPercent.times(100).toString();
        return `降价:${reductionPercent}%`;
    }

    /**
     * 设置当前价格
     * @param {*} price
     */
    currentPriceSet( price ) {
        this.priceCurrent = price;
    }

    /**
     *  获取买入操作是否成功
     * @returns {boolean}
     */
    isSuccess() {
        return null !== this.orderInfo;
    }

    /**
     * 执行买入操作
     * @returns {Promise<void>}
     */
    async execute() {
        await this.buy();

        let hasProcessed = false;
        do { // 监视订单状态
            hasProcessed = await this.orderStateMonitorAndProcess();
        } while( !hasProcessed );

        return this.orderInfo;
    }

    /**
     * 买入下单
     * @private
     * @returns {Promise<void>}
     */
    async buy() {
        try {
            this.priceBase = this.priceCurrent;

            let orderParams = {};
            orderParams.instId = this.trade.options.instId;
            orderParams.tdMode = 'cash';
            orderParams.side = 'buy';
            if ( this.priceReductionPercent.equals(0) ) {
                // 市价下单
                orderParams.ordType = 'market';
                orderParams.sz = this.tradeTotalPrice.toString();
            } else {
                // 限价下单
                orderParams.ordType = 'limit';
                this.priceTarget = this.priceBase.minus(this.priceBase.mul(this.priceReductionPercent));
                orderParams.px = this.priceTarget.toString();
                orderParams.sz = this.tradeTotalPrice.div(this.priceTarget).toString();
            }

            let service = OkxRestService.getInstance();
            let order = await service.tradeOrderCreate(orderParams);
            this.orderId = order.ordId;
            this.orderStartedAt = Date.now();
            this.log(`买入下单成功 : ordId = ${order.ordId}`);
            this.trade.statusSet('buy-executing');
        } catch ( e ) {
            throw new Error(`买入下单失败: ${e.message}`);
        }
    }

    /**
     * monitor order status and process
     * @private
     * @returns {Promise<void>}
     */
    async orderStateMonitorAndProcess() {
        let instId = this.trade.options.instId;
        let service = OkxRestService.getInstance();
        let orderInfo = await service.tradeOrderGet(instId, this.orderId);

        // 订单状态为挂单
        if ( 'live' === orderInfo.state ) {
            return await this.orderStateMonitorAndProcessStatusLive(orderInfo);
        }

        // 订单状态为成交
        if (orderInfo.state == 'filled' ) {
            this.orderInfo = orderInfo;
            this.orderFilledAt = Date.now();
            this.priceBought = orderInfo.fillPx;
            this.log('买入成交');
            this.trade.statusSet('buy-completed');
            this.trade.emit('buy-complete', this.trade);
            return true;
        }

        // 订单被关闭，这里可能是被手动撤单
        if ( orderInfo.state == 'canceled' ) {
            this.isCanceled = true;
            this.log('买入撤单');
            this.trade.statusSet('buy-canceled');
            this.trade.emit('buy-cancel', this.trade, this.cancelReason);
            return true;
        }

        // 订单状态为部分成交
        if ( 'partially_filled' === orderInfo.state ) {
            this.orderFilledSize = orderInfo.fillSz;
            this.log(`买入部分成交 : ${orderInfo.fillSz}/${orderInfo.sz}`);
            this.trade.statusSet('buy-partially-filled');
            await Common.sleep(1000);
            return false ;
        }

        // 订单状态未知
        throw new Error(`买入订单状态未知 : ${orderInfo.state}`);
    }

    /**
     * 监视并处理挂单状态的订单
     * @return {Promise<boolean>}
     */
    async orderStateMonitorAndProcessStatusLive(orderInfo) {
        // 检查挂单时间是否超时, 挂单时间超时则撤销订单
        let duration = Math.floor((Date.now() - this.orderStartedAt));
        if ( 0 < this.orderDurationLimit && duration > this.orderDurationLimit ) {
            this.log(`挂单超时 : ${duration/1000}秒`);
            this.cancelReason = 'timeout';
            await this.cancel();
            return true;
        }

        // 检查价格差是否超出范围 : (当前价格 - 基础价格) / (基础价格 - 买入价格)
        // 当价格差距太大时，涨的太多，要么长时间不会降下来， 要么突然掉下来击穿卖出时的止损价格
        let currentPriceDiff = this.priceCurrent.minus(this.priceBase);
        if ( this.orderCancelPriceDiff > 0 && currentPriceDiff.greaterThanOrEqualTo(0) ) {
            let boughtPriceDiff = (new Decimal(orderInfo.px)).minus(this.priceBase).abs();
            let diffTimes = currentPriceDiff.div(boughtPriceDiff);
            if ( diffTimes.greaterThanOrEqualTo(this.orderCancelPriceDiff) ) {
                this.cancelReason = 'price-diff-over-range';
                this.log(`价格差超出范围`);
                await this.cancel();
                return true;
            }
        }

        // 正常挂单，继续监视
        this.log(`挂单中 价格差=${currentPriceDiff.toString()}; 耗时=${duration/1000}秒`);
        this.trade.emit('update', this.trade);
        await Common.sleep(1000);
        return false;
    }

    /**
     * 撤销订单
     * @public
     * @returns {Promise<void>}
     */
    async cancel() {
        try {
            let instId = this.trade.options.instId;
            let service = OkxRestService.getInstance();
            await service.tradeOrderCancel(instId, this.orderId);
            this.isCanceled = true;
            this.log(`买入撤单成功`);
        } catch ( e ) {
            throw new Error(`买入撤单失败: ${e.message}`);
        }
    }

    /**
     * 日志打印
     * @param  {...any} args
     */
    log( ... args ) {
        let prefix = [];
        prefix.push('RoundTripTradeBuyerDefault');
        if ( null !== this.orderId ) {
            prefix.push(`ordId=${this.orderId}`);
        }
        prefix = `[${prefix.join(' ')}]`;
        args.unshift(prefix);
        console.log( ...args );
    }
}
